package spring.aop;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

import org.springframework.aop.Advisor;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.SpringProxy;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.core.DecoratingProxy;

/**
 * 基于 Spring AOP API 构建的一个非常简单的 Spring AOP的环境，用以源码剖析：JdkDynamicAopProxy。
 * 见：https://blog.csdn.net/f641385712/article/details/88952482
 *
 * @author ME
 */
public class SpringAopApi {

    public static void main(String[] args) {
        ProxyFactory proxyFactory = new ProxyFactory(new DemoImpl());



        // ------------- MethodBeforeAdvice
        proxyFactory.addAdvice((AfterReturningAdvice) (returnValue, method, args1, target) -> {
            System.out.println("[AfterReturningAdvice444] 你被拦截了，返回值为：" + returnValue);
        });



        // ------------- MethodBeforeAdvice
        Advisor advisor111 = new DefaultPointcutAdvisor(new BeforeAdvice111());
        proxyFactory.addAdvisor(advisor111);

        Advisor advisor222 = new DefaultPointcutAdvisor(new BeforeAdvice222());
        proxyFactory.addAdvisor(advisor222);



        // ------------- AfterReturningAdvice
        Advisor advisor333 = new DefaultPointcutAdvisor(new AfterReturningAdvice333());
        proxyFactory.addAdvisor(advisor333);
        // 通过类型转换告诉编译器是 什么类型的 FunctionInterface
        proxyFactory.addAdvice((AfterReturningAdvice) (returnValue, method, args1, target) -> {
            System.out.println("[AfterReturningAdvice444] 你被拦截了，返回值为：" + returnValue);
        });

        IDemo demo = (IDemo) proxyFactory.getProxy();
        // 你被拦截了：方法名为：hello 参数为--[]
        // this demo show
        demo.hello();

        System.out.println("\n----------------------- toString ---------------------------------");
        System.out.println(demo.toString());

        System.out.println();
        //class spring.AOP.SpringAopApi$Demo
        System.out.println(proxyFactory.getTargetClass());
        //SingletonTargetSource for target object [spring.AOP.SpringAopApi$Demo@643b1d11]
        System.out.println(proxyFactory.getTargetSource());
        //[interface spring.AOP.SpringAopApi$DemoInterface]
        System.out.println(Arrays.asList(proxyFactory.getProxiedInterfaces()));
        //[org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [Pointcut.TRUE]; advice [spring.AOP.SpringAopApi$$Lambda$2/1349414238@2ef5e5e3]]
        System.out.println(Arrays.asList(proxyFactory.getAdvisors()) + "\n");

        // 获取类型，看看是 JDK代理 还是 cglib的
        System.out.println(demo instanceof Proxy); //true  所有的 JDK代理都继承自 java.lang.reflect.Proxy
        System.out.println(demo instanceof SpringProxy); //true
        System.out.println(demo.getClass()); //class spring.AOP.$Proxy0
        System.out.println(Proxy.isProxyClass(demo.getClass())); //true
        System.out.println(AopUtils.isCglibProxy(demo) + "\n"); //false

        //测试Advised接口、DecoratingProxy的内容
        Advised advised = (Advised) demo;
        System.out.println(Arrays.asList(advised.getProxiedInterfaces())); //[interface spring.AOP.SpringAopApi$DemoInterface]
        System.out.println(Arrays.asList(advised.getAdvisors())); //[org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [Pointcut.TRUE]; advice [spring.AOP.SpringAopApi$$Lambda$2/1349414238@2ef5e5e3]]
        System.out.println(advised.isExposeProxy()); //false
        System.out.println(advised.isFrozen() + "\n"); //false

        // System.out.println(advised.removeAdvice(null));
        DecoratingProxy decoratingProxy = (DecoratingProxy) demo;
        System.out.println(decoratingProxy.getDecoratedClass() + "\n"); //class spring.AOP.SpringAopApi$Demo

        System.out.println("-----------------------------------------------------");

        // Object的方法 ==== 所有的Object方法都不会被AOP代理 这点需要注意
        System.out.println(demo.equals(new Object()));
        System.out.println(demo.hashCode());
        System.out.println(demo.getClass() + "\n");

        // 其余方法都没被拦截  只有toString()被拦截了  咋回事呢？它也不符合切点表达式的要求啊  看下面的解释吧
        // 你被拦截了：方法名为：hello 参数为--[]
        // spring.AOP.SpringAopApi$Demo@643b1d11
        System.out.println(demo.toString());
    }

}

/** 目标类的接口 */
interface IDemo {
    void hello();
}

/** 目标类 */
class DemoImpl implements IDemo {
    @Override
    public void hello() {
        System.out.println("demo say hello~~");
    }
}

/** 方法前的增强111 */
class BeforeAdvice111 implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("[BeforeAdvice111] 你被拦截了，方法名为：" + method.getName() + "，参数为：" + Arrays.asList(args));
    }
}

/** 方法前的增强222 */
class BeforeAdvice222 implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("[BeforeAdvice222] 又又又被拦截了");
    }
}

/** 方法正常返回时的增强 */
class AfterReturningAdvice333 implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("[AfterReturningAdvice333] 你被拦截了，返回值为：" + returnValue);
    }
}